#include "CameraController.h"
#include <corecrt_math_defines.h>
#include "Camera.h"
#include "Core/InputTypes.h"

namespace SL
{
CameraController::CameraController(Camera* pCam)
{
    pCamera = pCam;
}

void OrbiterCameraController::setModelParams(const float3& center, float radius, float zoomRadius)
{
    this->orbitCenter = center;
    this->orbitRadius = radius;
    this->zoomRadius = zoomRadius;
    bDirty = true;
}

bool OrbiterCameraController::update(float dt)
{
    if (!bDirty && !pCamera)
        return false;

    updateRotation(dt);

    float3 cameraPos = orbitCenter + (accRotQuat * float3(0, 0, -1)).normalized() * zoomRadius * orbitRadius;
    pCamera->setPosition(cameraPos);

    // possible panning
    pCamera->setTarget(orbitCenter);

    pCamera->setUpVector(accRotQuat * float3(0, 1, 0));

    bDirty = false;

    return true;
}

void OrbiterCameraController::updateRotation(float dt)
{
    /*
        Even though no delta time involed
        - sensitivity is much slower in VSync ON (low framerate)
        - mouse moving fast cause flipping rotation

        *SOLVED: AngleAxis 's axis must be normalized
    */
    // float max_angle = M_PI_2;
    // float angle = std::max(std::min(max_angle, sensitivity * accDelta.norm()),- max_angle);
    accDelta.y() /= pCamera->getAspectRatio();
    float delta = accDelta.norm();
    if (delta > 1e-10)
    {
        AngleAxis step_aa = Eigen::AngleAxisf(sensitivity * delta, float3(accDelta.y(), accDelta.x(), 0.0) / delta);

        accRotQuat = (accRotQuat * Quaternion(step_aa)).normalized();
        accDelta = float2(0, 0);
    }
}

bool OrbiterCameraController::onMouseEvent(const MouseEvent& mouseEvent)
{
    bool handled = false;

    switch (mouseEvent.type)
    {
    case MouseEvent::Type::Wheel:
        orbitRadius -= (mouseEvent.wheelDelta.y() * 0.2f);
        bDirty = true;
        handled = true;
        break;

    case MouseEvent::Type::ButtonDown:
        if (mouseEvent.button == Input::MouseButton::Left)
        {
            lastPos = mouseEvent.pos;
            isRotating = true;
        }
        else if (mouseEvent.button == Input::MouseButton::Right)
        {
            // panning
        }
        handled = true;
        break;

    case MouseEvent::Type::ButtonUp:
        if (mouseEvent.button == Input::MouseButton::Left)
        {
            handled = isRotating;
            isRotating = false;
        }
        break;

    case MouseEvent::Type::Move:
        if (isRotating)
        {
            // based on mouse length
            float2 curPos = mouseEvent.pos;
            float2 move2D = curPos - lastPos;
            accDelta += move2D;
            bDirty = true;
            lastPos = curPos;

            handled = true;
        }
        break;
    default:
        break;
    }

    return handled;
}

} // namespace SL